package top.jacktgq.concurrent_utils.pool;
import java.time.DayOfWeek;
import java.time.Duration;
import	java.time.temporal.ChronoUnit;

import top.jacktgq.utils.LogUtils;
import top.jacktgq.utils.Sleeper;

import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjusters;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.*;

/**
 * @Author CandyWall
 * @Date 2020/11/4--19:11
 * @Description 利用线程池完成多线程任务调度
 */
public class ScheduledThreadPoolDemo {
    public static void main(String[] args) {
        //testTimer();
        //testScheduledThreadPool1();
        //testScheduledThreadPool2();
        //testScheduledThreadPool3();
        demo();
    }

    //案例一：如何让每周四 18:00:00 定时执行任务？
    public static void demo() {
        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(1);
        //计算当前的时间和星期四之间的时间差
        LocalDateTime now = LocalDateTime.now();
        LogUtils.log("demo", now.toString());
        LocalDateTime nextThursDay = now.withHour(18).withMinute(0).withSecond(0).withNano(0).with(DayOfWeek.THURSDAY);
        //如果当前时间超过了本周的周四
        if (now.compareTo(nextThursDay) > 0) {
            nextThursDay.plusWeeks(1);
        }
        LogUtils.log("demo", nextThursDay.toString());

        // long delay = Duration.between(now, nextThursDay).toMillis();
        long delay = ChronoUnit.MILLIS.between(now, nextThursDay);
        LogUtils.log("demo", "" + delay);

        threadPool.scheduleAtFixedRate(() -> {
            LogUtils.log("demo", "hello world");
        }, delay, 7 * 24 * 3600 * 1000, TimeUnit.MILLISECONDS);
    }

    //juc工具包下提供的多线程任务调度的api，多个任务之间不会有任何影响
    public static void testScheduledThreadPool1() {
        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(2);
        LogUtils.log("testScheduledThreadPool1", "start...");
        //
        ScheduledFuture<Boolean> future = threadPool.schedule(() -> {
            LogUtils.log("testScheduledThreadPool1", "Task 1 is running");
            //Sleeper.sleep(2);
            //在ScheduledThreadPool线程池中运行的任务如果有异常，
            //第一种处理异常的方式：自己捕获异常
            /*try {
                int i = 1 / 0;
            } catch (Exception e) {
                e.printStackTrace();
            }*/
            int i = 1 / 0;
            LogUtils.log("testScheduledThreadPool1", "Task 1 is finish");
            //第二种处理异常的方式：在任务中加一个返回值，兰姆达表达式会解析成Callable，可以接收一个Future对象
            return true;
        }, 1, TimeUnit.SECONDS);
        try {
            //future对象在正常情况下会获取到正常的返回值，但是任务的执行过程中有异常的话，它会返回异常信息
            LogUtils.log("testScheduledThreadPool1", "" + future.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
        //在一定的时间间隔之后再去执行任务，只执行一次
        threadPool.schedule(() -> {
            LogUtils.log("testScheduledThreadPool1", "Task 2 is running");
        }, 1, TimeUnit.SECONDS);
    }

    public static void testScheduledThreadPool2() {
        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(2);
        LogUtils.log("testScheduledThreadPool2", "start...");
        //按照一定的周期去循环执行某个任务，如果某个任务内部本身的执行时间超过这个周期，
        //那么要确保当前这一次的任务必须执行完才能进行下一次周期
        threadPool.scheduleAtFixedRate(() -> {
            LogUtils.log("testScheduledThreadPool2", "Task 1 is running");
            Sleeper.sleep(2);
            //int i = 1 / 0;
            LogUtils.log("testScheduledThreadPool2", "Task 1 is finish");
        }, 1, 1, TimeUnit.SECONDS);
    }

    public static void testScheduledThreadPool3() {
        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(2);
        LogUtils.log("testScheduledThreadPool3", "start...");
        //按照一定的时间间隔去循环执行某个任务，这个时间间隔是从前一次任务执行完到下一次任务开始
        threadPool.scheduleWithFixedDelay(() -> {
            LogUtils.log("testScheduledThreadPool3", "Task 1 is running");
            Sleeper.sleep(2);
            //int i = 1 / 0;
            LogUtils.log("testScheduledThreadPool3", "Task 1 is finish");
        }, 1, 1, TimeUnit.SECONDS);
    }

    //旧的时间调度api Timer类，只支持单线程任务的时间调度，多个任务的话会串行执行
    //上一个任务的延迟会影响下一个任务，并且上一个任务执行的过程中有异常，会导致下一个任务也中断
    private static void testTimer() {
        Timer timer = new Timer();
        TimerTask task1 = new TimerTask() {
            @Override
            public void run() {
                LogUtils.log("testTimer", "Task 1 is running");
                //Sleeper.sleep(2);
                int i = 1 / 0;
            }
        };
        TimerTask task2 = new TimerTask() {
            @Override
            public void run() {
                LogUtils.log("testTimer", "Task 2 is running");
            }
        };

        LogUtils.log("testTimer", "start...");
        timer.schedule(task1, 1000);
        timer.schedule(task2, 1000);
    }
}
